process.env.NODE_ENV = 'production'

/* argv */
const argv = require('./argv')
/* const */
const { ALL } = require('./model/const')
/* model */
const { ModuleAutoBuildOption, ModuleBuildOption } = require('./model/BuildOption')
const modules = require('../modules')
const BuildOption = require('./model/BuildOption')
const { GIT_EXIST, GIT_STATUS_PORCELAIN, GIT_CURRENT_BRANCH_COMMAND } = require('./model/command')
/* production */
const { productionBuild } = require('./production')
const { productionSpaBuild } = require('./build-spa')
/* webpack */
const WebpackProductionConfig = require('../config/webpack.prod.conf')
// https://github.com/jantimon/html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
/* util */
const { isEmpty } = require('lodash')
const colors = require('colors')
const util = require ('../config/util')
const { getEntryMap } = require('./util/get-increment-build-entry')
const getBuildOptionWithInquirer = require('./util/get-build-option-with-inquirer')
const replaceToEsBuild = require('./util/replace-to-esbuild-loader')
const { exit, command } = require('./util/common')
const { getCurrentBranch } = require('./util/git')
const getNotificationEntryName = require('./util/get-notification-entry-name')
const { notificationStart } = require('./util/notification')
const shell = require('shelljs')
/* 用户配置 */
const argvData = argv(process.argv.slice(2))
const user = argvData.user || 'huangbc'
const userConfig = require(`./config/${user}`)

async function build(option) {
  
  const config = userConfig
  // web项目静态资源路径
  const path = config.targetRootPath
  
  // 设置打包路径
  option.path = path
  
  // 生产环境webpack配置
  const webpackProductionConfig = WebpackProductionConfig
  const moduleBuildOptions = option ? option.moduleBuildOptions : new ModuleBuildOption()
  const moduleAutoBuildOption = moduleBuildOptions.moduleAutoBuildOption || new ModuleAutoBuildOption()
  
  option.userName = user
  
  // 开启使用esbuild
  if (option.isUseEsBuild) {
    useEsBuild(webpackProductionConfig)
  }
  
  // 全量打包
  if (option.isFullBuild) {
    return fullBuild(webpackProductionConfig, option)
  }
  
  // 按模块打包 手动选择模块构建
  if (option.isModuleBuild && moduleBuildOptions.isModuleManualBuild) {
    const modulesManualKeys = moduleBuildOptions.modulesManualKeys || []
    return moduleManualBuild(webpackProductionConfig, modulesManualKeys, option)
  }
  
  // 按模块打包 自动分析构建 按分支
  if (option.isModuleBuild && moduleBuildOptions.isModuleAutoBuild && moduleAutoBuildOption.isBranch) {
    const branch = moduleAutoBuildOption.branch
    return moduleAutoBuildForBranch(webpackProductionConfig, branch, option)
  }
  
  // 按模块打包 自动分析构建 按提交
  if (option.isModuleBuild && moduleBuildOptions.isModuleAutoBuild && moduleAutoBuildOption.isCommit) {
    const commit = moduleAutoBuildOption.commit
    return moduleAutoBuildForCommit(webpackProductionConfig, commit, option)
  }
  
}

async function buildSpa(buildOption) {
  
  notificationStart('单页面 全量构建 ~~~~~~')
  
  productionSpaBuild(buildOption)
  
}

function fullBuild(webpackProductionConfig, buildOption) {
  
  notificationStart('多页面 全量构建 ~~~~~~')
  
  productionBuild(webpackProductionConfig, buildOption)
}

function moduleManualBuild(webpackProductionConfig, modulesManualKeys, buildOption) {
  
  // 选择的模块为空，则默认全量打包
  if (isEmpty(modulesManualKeys)) {
    
    fullBuildLog()
    
    buildOption.isFullBuild = true
    
    return fullBuild(webpackProductionConfig, buildOption)
  }
  
  // 原始入口数据
  let originEntry = modules
  // 新入口数据
  let entry = {}
  
  // 添加新入口数据
  Object.keys(originEntry).forEach(key => {
    if (modulesManualKeys.includes(key)) {
      entry[key] = originEntry[key]
    }
  })
  
  let plugins = webpackProductionConfig.plugins || []
  plugins = plugins.filter(plugin => {
    return !(plugin instanceof HtmlWebpackPlugin)
  })
  
  webpackProductionConfig.plugins = plugins.concat(util.genHtmlPlugins(entry))
  // 用新入口配置数据覆盖配置项
  webpackProductionConfig.entry = util.genEntry(entry)
  
  const content = getNotificationEntryName(entry)
  notificationStart(content)
  
  // 启动
  productionBuild(webpackProductionConfig, buildOption)
}

async function moduleAutoBuildForBranch(webpackProductionConfig, branch, buildOption) {
  const entryMap = await getEntryMap(branch)
  
  // 用新入口配置数据覆盖配置项
  webpackProductionConfig.entry = util.genEntry(entryMap)
  
  const content = getNotificationEntryName(entryMap)
  notificationStart(content)
  
  // 构建
  productionBuild(webpackProductionConfig, buildOption)
}

async function moduleAutoBuildForCommit(webpackProductionConfig, commit, buildOption) {
  const entryMap = await getEntryMap(commit)
  
  // 用新入口配置数据覆盖配置项
  webpackProductionConfig.entry = util.genEntry(entryMap)
  
  const content = getNotificationEntryName(entryMap)
  notificationStart(content)
  
  // 构建
  productionBuild(webpackProductionConfig, buildOption)
}

function useEsBuild(webpackProductionConfig) {
  replaceToEsBuild(webpackProductionConfig)
}

function fullBuildLog() {
  console.log(`${colors.red('   ')}`)
  console.log(`${colors.red('  Caused: 未选择模块，默认全量打包 !!!')}`)
  console.log(`${colors.red('    ')}`)
}

/** 
 * @description 服务端构建
*/
function buildByCloud() {
  
  // 初始化打包配置信息
  const buildOption = new BuildOption()
  
  buildOption.isCloudBuild = true
  // 服务端构建默认全量打包
  buildOption.isFullBuild = true
  
  const currentBranch = getCurrentBranch()
  const spaBranches = ['pctest', 'pre_master_20220616']
  
  if (spaBranches.includes(currentBranch)) {
    
    const config = userConfig
    // web项目静态资源路径
    const path = config.targetRootPath
    
    // 设置打包路径
    buildOption.path = path
    
    buildOption.userName = user
    
    return buildSpa(buildOption)
  }
  
  build(buildOption)
}

/** 
 * @description 本地构建
*/
async function buildByLocal() {
  // 手动选择获取打包配置信息
  const buildOption = await getBuildOptionWithInquirer()
  
  build(buildOption)
}

/** 
 * @description 校验
*/
function validate() {
  
  const config = userConfig
  // web项目静态资源路径
  const path = config.targetRootPath
  
  validateShbWebResourcePathExist(path)
  
  validateShbWebResourcePathIsGit(path)
  
  validateShbWebResourceGitHandler(path)
  
}

/** 
 * @description 校验 shb-web 静态资源路径是否存在
*/
function validateShbWebResourcePathExist(path) {
  if (!path) {
    console.log(`${
      colors.red(`
      ERROR: 没有找到对应的 shb-web-resource 仓库资源目录 !!!!!!!!
      请确保 /script/config/ 目录下对应用户配置下包含 targetRootPath 属性`)
    }`)
    exit()
  }
}

/** 
 * @description 校验 shb-web-resource 静态资源路径是否为 git仓库
*/
function validateShbWebResourcePathIsGit(path) {
  
  const isDir = shell.test('-d', path)
  
  if (!isDir) {
    console.log(`${
      colors.red(`
      ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性为目录路径 !!!!!!!!`)
    }`)
    exit()
  }
  
  shell.cd(path)
  
  const isGit = shell.exec(GIT_EXIST, { silent : true }).trim() == 'true'
  
  if (!isGit) {
    console.log(`${
      colors.red(`
      ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性目录路径为 git仓库 !!!!!!!!`)
    }`)
    exit()
  }
  
}

/** 
 * @description 校验 shb-web-resource git相关的操作
*/
function validateShbWebResourceGitHandler(path) {
  
  
  shell.cd(path)
  
  const isNotGitClean = shell.exec(GIT_STATUS_PORCELAIN, { silent : true }).trim()
  
  if (isNotGitClean) {
    console.log(`${
      colors.red(`
      ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性对应的git仓库 工作区是干净的`)
    }`)
    exit()
  }
  
  const currentShbWebResourceBranch = shell.exec(GIT_CURRENT_BRANCH_COMMAND, { silent : true }).trim()
  
  shell.cd(`${__dirname}/../`)
  
  const currentBranch = command(GIT_CURRENT_BRANCH_COMMAND).trim()
  const isAllotNotSameBranch = argvData.sameBranch === 'false'
  
  if (isAllotNotSameBranch) {
    return
  }
  
  if (currentBranch != currentShbWebResourceBranch) {
    console.log(`${
      colors.red(`
      ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性对应的git仓库分支 与 当前 shb-pc-fe 的分支是一致的 ！！！！！
      如果需要开启不一致的分支，请在当前 npm执行的脚本对应的后面的命令后面加上 --sameBranch=false 即可。
      
      示例：
      "build:hbc": "git pull && node --max-old-space-size=18196 script/build.js --user=huangbc --sameBranch=false",
      `)
    }`)
    exit()
  }
  
}

/** 
 * @description 主函数入口
*/
async function main() {
  
  try {
    
    validate()
    
    if (argvData.cloud) {
      buildByCloud()
    } else {
      buildByLocal()
    }
    
  } catch (error) {
    console.error(error)
  }
  
}

main()